﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Text.RegularExpressions;

namespace ClearNugetPackage
{
	public class Program
	{

		#region 属性

		/// <summary>
		/// packagehome的值
		/// </summary>
		private static string packageHomeValue = string.Empty;

		#endregion

		#region 变量

		/// <summary>
		/// 环境变量名称
		/// </summary>
		private const string c_PackageHome = "PACKAGEHOME";

		/// <summary>
		/// v3_cahe环境变量名称
		/// </summary>
		private const string c_NUGET_HTTP_CACHE_PATH = "NUGET_HTTP_CACHE_PATH";

		/// <summary>
		/// linq所在文件夹
		/// </summary>
		private const string c_nuget_groupFileSuffix = "nuget-group_index.json";

		/// <summary>
		/// linq缓存文件名称
		/// </summary>
		private const string c_list_urp_eframework_dallinq = "list_urp.eframework.dallinq.dat";

		/// <summary>
		/// data缓存文件名称
		/// </summary>
		private const string c_list_urp_common_data = "list_urp.common.data.dat";

		/// <summary>
		/// business缓存文件名称
		/// </summary>
		private const string c_list_urp_common_business = "list_urp.common.business.dat";

		/// <summary>
		/// v3_cache缓存配置参数开头
		/// </summary>
		private const string c_http_cache_header = "http-cache:";

		/// <summary>
		/// 本地仓库存储文件名称
		/// </summary>
		private const string c_LocalWarehouseFileName = "LocalWarehouse.txt";

		/// <summary>
		/// NuGet文件夹名称
		/// </summary>
		private const string c_nugetDirecotry = ".nuget";

		/// <summary>
		/// NuGet.exe
		/// </summary>
		private const string c_NuGetExe = "NuGet.exe";

		#endregion

		#region 方法

		static void Main(string[] args)
		{
			try
			{
				Console.WriteLine("nupkg本地包缓存文件清理开始..............");

				#region 通过dotnet命令获取http_cache路径

				string cmd = "dotnet nuget locals http-cache --list";
				ExecuteCmdResult result = GetCmdOutput(cmd);

				//检测是否正常退出，若异常退出则提示消息
				if (result?.ExitCode != 0)
				{
					Console.WriteLine($"Exit Code: {result.ExitCode},错误信息：{result.ErrorMessage}");

					Console.WriteLine("请先检查dotnet是否安装，如未安装，请按下列提示操作！");
					Console.WriteLine();
					Console.WriteLine("方法一：使用 Visual Studio Installer \r\n" +
											  "1.打开 Visual Studio Installer \r\n" +
											  "2.点击“修改”按钮（或“安装”按钮，如果尚未安装 Visual Studio） \r\n" +
											  "3.在“个人”或“其他工具和框架”部分，找到“.NET 跨平台开发”选项 \r\n" +
											  "4.勾选“.NET 桌面开发”和“.NET Core 跨平台开发”等相关的选项 \r\n" +
											  "5.点击“修改”按钮进行安装 \r\n"
											  );

					Console.WriteLine();
					Console.WriteLine("方法二：手动下载和安装\r\n" +
										  "1. Microsoft 的官方网站下载 .NET SDK 的安装包 \r\n" +
										  "2.选择合适的 .NET SDK 版本（例如，最新版本或 LTS 版本） \r\n" +
										  "3.根据你的操作系统（Windows x64, Windows x86 等），下载对应的安装包 \r\n" +
										  "4.运行下载的安装包并按照指示完成安装 \r\n"
										  );

					Console.WriteLine();

					Console.WriteLine("验证安装");
					Console.WriteLine();
					Console.WriteLine("安装完成后在命令提示符（cmd）或 PowerShell 中运行 [dotnet --version] 命令");

					Console.WriteLine();
					Console.WriteLine("nupkg本地包缓存文件清理失败..............");
					Console.ReadKey();
					return;
				}

				#endregion

				//先通过命令行获取http-cache缓存配置
				string v3_cachePath = result.ReturnData;

				#region 解析路径

				// 解析路径
				var caches = result?.ReturnData.Split('\n');
				foreach (var line in caches)
				{
					if (!line.Contains(c_http_cache_header))
					{
						continue;
					}

					v3_cachePath = line.Replace(c_http_cache_header, "").Replace("\r", "");
					break;
				}

				#endregion

				//若两次获取缓存设置失败，则提示是否添加环境变量
				if (string.IsNullOrEmpty(v3_cachePath))
				{
					#region 检测是否以管理员权限运行

					if (!IsAdministrator())
					{
						Console.WriteLine("请以管理员权限运行此程序");
						return;
					}

					#endregion

					#region 提示是否添加环境变量

					Console.WriteLine($"环境变量{c_NUGET_HTTP_CACHE_PATH}未配置！");
					Console.WriteLine();
					Console.WriteLine("请自行配置环境变量 或 按后续导引进行配置 后重新启动程序");
					Console.WriteLine();
					Console.WriteLine("是否按后续导引进行配置环境变量？Y(是) | N（否）");
					var key = Console.ReadKey();
					char input = char.ToUpper(key.KeyChar);
					while (!(input == 'Y' || input == 'N'))
					{
						Console.WriteLine();
						Console.WriteLine("错误：输入不正确，请输入 Y 或 N ");

						key = Console.ReadKey();
						input = char.ToUpper(key.KeyChar);
					}

					Console.WriteLine();

					//判断是否取消添加环境变量
					if (input == 'N')
					{
						Console.WriteLine();
						Console.WriteLine("环境变量添加失败！");
						return;
					}

					#endregion

					#region 添加系统环境变量

					Dictionary<string, string> environmentVariables = new Dictionary<string, string>();

					Console.WriteLine();
					Console.WriteLine("请输入 [PACKAGEHOME] 环境变量的值！");
					string output1 = Console.ReadLine();
					packageHomeValue = output1;
					environmentVariables.Add(c_PackageHome, output1);

					Console.WriteLine();
					Console.WriteLine("请输入 [NUGET_HTTP_CACHE_PATH] 环境变量的值！");
					string output2 = Console.ReadLine();
					environmentVariables.Add(c_NUGET_HTTP_CACHE_PATH, output2);

					Console.WriteLine();
					Console.WriteLine("是否确认添加环境变量(Y|N)：\r\n" + $"1. [{c_PackageHome}]:[{output1}] \r\n" + $"2. [{c_NUGET_HTTP_CACHE_PATH}]:[{output2}]");
					key = Console.ReadKey();
					input = char.ToUpper(key.KeyChar);
					while (!(input == 'Y' || input == 'N'))
					{
						Console.WriteLine();
						Console.WriteLine("错误：输入不正确，请输入 Y 或 N ");

						key = Console.ReadKey();
						input = char.ToUpper(key.KeyChar);
					}

					//判断是否取消添加环境变量
					if (input == 'N')
					{
						Console.WriteLine();
						Console.WriteLine("环境变量添加失败！");
						return;
					}
					#endregion

					#region 执行添加环境变量、创建环境变量所选目录

					int i = 0;
					foreach (var environment in environmentVariables)
					{
						#region 在选择文件夹下创建目录

						string variableName = environment.Key;
						string variableValue = environment.Value;
						if (string.IsNullOrEmpty(variableValue))
						{
							continue;
						}

						//创建目录
						CreateEnviromentDiretory(variableValue);

						#endregion

						#region 添加环境变量

						//检测是否存在此环境变量
						if (!string.IsNullOrEmpty(GetEnvironmentVariable(variableName)))
						{
							continue;
						}

						//添加系统环境变量
						GeneratorEnvironmentVariable(variableName, variableValue);

						i++;

						#endregion
					}

					Console.WriteLine();
					Console.WriteLine(i == 0 ? "环境变量已存在!" : "环境变量配置成功，重启电脑后环境变量配置生效！");

					#endregion
				}
				else
				{
					#region 删除本地缓存文件

					if (!Directory.Exists(v3_cachePath))
					{
						Console.WriteLine($"{v3_cachePath}文件目录不存在！");
						return;
					}

					var directories = Directory.GetDirectories(v3_cachePath);
					if (directories?.Count() == 0)
					{
						return;
					}

					foreach (var directory in directories)
					{
						if (!directory.EndsWith(c_nuget_groupFileSuffix))
						{
							continue;
						}

						var files = Directory.GetFiles(directory);
						if (files?.Count() == 0)
						{
							continue;
						}

						//删除linq缓存文件
						string linqPath = Path.Combine(directory, c_list_urp_eframework_dallinq);
						if (File.Exists(linqPath))
						{
							File.Delete(linqPath);
						}
						//删除data缓存文件
						string dataPath = Path.Combine(directory, c_list_urp_common_data);
						if (File.Exists(dataPath))
						{
							File.Delete(dataPath);
						}
						//删除business缓存文件
						string businessPath = Path.Combine(directory, c_list_urp_common_business);
						if (File.Exists(businessPath))
						{
							File.Delete(businessPath);
						}
					}

					Console.WriteLine("nupkg本地包清理成功,请重新生成项目！");

					#endregion
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}

			Console.ReadKey();
		}

		/// <summary>
		/// 通过命令行获取缓存配置
		/// </summary>
		/// <returns></returns>
		private static string GetEnvironmentVariableByCmd()
		{
			try
			{
				#region 使用dotnet命令查找http_cache

				string cmd = "dotnet nuget locals http-cache --list";
				ExecuteCmdResult result = GetCmdOutput(cmd);
				if (result == null)
				{
					return string.Empty;
				}

				//检测是否正常退出，若异常退出则提示消息
				if (result.ExitCode != 0)
				{
					Console.WriteLine($"Exit Code: {result.ExitCode},错误信息：{result.ErrorMessage}");
					return string.Empty;
				}


				#endregion

				#region donet命令异常，使用NuGet查找http_cache

				if (string.IsNullOrEmpty(result.ReturnData))
				{
					#region 获取本地仓库存储位置

					//读取当前程序运行目录下的本地仓库路径存储文件
					string fileContent = string.Empty;
					string appRunDir = AppDomain.CurrentDomain.BaseDirectory;
					string filePath = Path.Combine(appRunDir, c_LocalWarehouseFileName);
					if (File.Exists(filePath))
					{
						//读取文件内容
						using (StreamReader sr = new StreamReader(filePath))
						{
							fileContent = sr.ReadToEnd();
						}
					}

					//NuGet.exe文件路径
					string nugetFilePath = string.Empty;

					#endregion

					#region 引导输入本地仓库位置,构建NuGet.exe的路径

					//若文件不存在或者读取的本地仓库路径为空，则引导输入GDXX本地仓库位置
					//并保存本地仓库位置至文件中
					if (string.IsNullOrEmpty(fileContent) || !Directory.Exists(fileContent))
					{
						#region 获取NuGet.exe的位置

						Console.WriteLine("请输入GDXX本地仓库位置！");
						string input = Console.ReadLine();
						nugetFilePath = Path.Combine(input, c_nugetDirecotry, c_NuGetExe);
						while (!File.Exists(nugetFilePath))
						{
							Console.WriteLine();
							Console.WriteLine("错误：输入的本地仓库路径不正确，请重新输入！ ");

							input = Console.ReadLine();
							nugetFilePath = Path.Combine(input, c_nugetDirecotry, c_NuGetExe);
						}

						//如果路径正确，则写入程序目录下的LocalWarehouse.txt文件中
						if (File.Exists(filePath))
						{
							File.WriteAllText(filePath, input);
						}
						else
						{
							using (FileStream fs = new FileStream(filePath, FileMode.Create))
							{
								using (StreamWriter writer = new StreamWriter(fs))
								{
									writer.Write(input);
								}
							}
						}

						#endregion
					}
					else
					{
						nugetFilePath = Path.Combine(fileContent, c_nugetDirecotry, c_NuGetExe);
					}

					#endregion

					#region 执行命令并或取返回值

					cmd = nugetFilePath + " locals http-cache -list";
					result = GetCmdOutput(cmd);

					#endregion
				}

				#endregion

				#region 解析返回值

				if (string.IsNullOrEmpty(result?.ReturnData))
				{
					return string.Empty;
				}

				// 解析结果示例
				string v3_cachePath = string.Empty;
				var caches = result?.ReturnData.Split('\n');
				foreach (var line in caches)
				{
					if (!line.Contains(c_http_cache_header))
					{
						continue;
					}

					v3_cachePath = line.Replace(c_http_cache_header, "").Replace("\r", "");
					break;
				}

				return v3_cachePath;

				#endregion
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
				return string.Empty;
			}
		}

		/// <summary>
		/// Process进程调用程序执行命令
		/// </summary>
		/// <param name="appName"></param>
		/// <param name="cmd"></param>
		/// <returns></returns>
		private static ExecuteCmdResult GetCmdOutput(string cmd)
		{
			if (string.IsNullOrEmpty(cmd))
			{
				return null;
			}

			var process = new Process
			{
				StartInfo = new ProcessStartInfo
				{
					FileName = "cmd.exe",
					UseShellExecute = false,
					RedirectStandardInput = true,
					RedirectStandardOutput = true,
					RedirectStandardError = true,
					CreateNoWindow = true,
				}
			};

			process.Start();//执行  
			process.StandardInput.WriteLine(cmd + "&exit");
			process.StandardInput.AutoFlush = true;  //提交  

			string output = process.StandardOutput.ReadToEnd();//输出  
			string errorOutput = process.StandardError.ReadToEnd();//输出错误信息
			process.WaitForExit();//等待程序执行完退出进程  

			ExecuteCmdResult result = new ExecuteCmdResult
			{
				ExitCode = process.ExitCode,
				ErrorMessage = errorOutput,
				ReturnData = output
			};
			process.Close();//结束  
			return result;
		}

		/// <summary>
		/// 获取packageHome环境变量的路径配置
		/// </summary>
		/// <returns></returns>
		private static string GetEnvironmentVariable(string enviromentVariableName)
		{
			string envirorment = Environment.GetEnvironmentVariable(enviromentVariableName);

			return envirorment;
		}

		/// <summary>
		/// 是否管理员权限运行
		/// </summary>
		/// <returns></returns>
		private static bool IsAdministrator()
		{
			return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
		}

		/// <summary>
		/// 创建文件目录
		/// </summary>
		/// <param name="variableValue"></param>
		/// <exception cref="Exception"></exception>
		private static void CreateEnviromentDiretory(string variableValue)
		{
			try
			{
				string path = string.Empty;

				//如果是以ConfigurationName开头的，构建文件目录
				if (IsMatchFixedBeginning(variableValue, c_PackageHome))
				{
					var paths = variableValue.Split('\\').ToList();
					if (paths?.Count() > 0)
					{
						for (int i = 0; i < paths.Count; i++)
						{
							//跳过第一个
							if (i == 0) { continue; }
							path += paths[i] + (i <= paths.Count - 1 ? "\\" : "");
						}
						path = Path.Combine(packageHomeValue, path);
					}
				}

				if (string.IsNullOrEmpty(path))
				{
					path = variableValue;
				}

				if (!Directory.Exists(path))
				{
					Directory.CreateDirectory(path);
				}
			}
			catch (Exception ex)
			{
				throw new Exception(ex.Message);
			}
		}

		/// <summary>
		/// 添加系统环境变量
		/// </summary>
		/// <param name="variableName"></param>
		/// <param name="variableValue"></param>
		/// <exception cref="Exception"></exception>
		private static void GeneratorEnvironmentVariable(string variableName, string variableValue)
		{
			try
			{
				using (RegistryKey envKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true))
				{
					envKey.SetValue(variableName, variableValue, RegistryValueKind.ExpandString);
				}
			}
			catch (Exception ex)
			{
				throw new Exception($" 系统变量 {variableName}-{variableValue} 创建失败: {ex.Message}");
			}
		}

		/// <summary>
		/// 匹配以某字符串开头
		/// </summary>
		/// <param name="input"></param>
		/// <param name="fixedBegin"></param>
		/// <returns></returns>
		private static bool IsMatchFixedBeginning(string input, string fixedBegin)
		{
			try
			{
				if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(fixedBegin))
				{
					return false;
				}

				return Regex.IsMatch(input, string.Format("^%{0}%", fixedBegin));
			}
			catch
			{
				return false;
			}
		}

		#endregion

		#region 内部类
		private class ExecuteCmdResult
		{
			/// <summary>
			/// 退出码
			/// </summary>
			public int ExitCode { get; set; }

			/// <summary>
			/// 命令返回数据
			/// </summary>
			public string ReturnData { get; set; }

			/// <summary>
			/// 错误信息
			/// </summary>
			public string ErrorMessage { get; set; }
		}

		#endregion
	}
}
